#!/bin/bash

me="$(basename "$0")"

##########################################################################
# USER CONFIGURATION

# please fill this space-separated  array to your liking. 
# each word stands for an application's class or classname that will be
# searched for, case insensitively, in xwininfo's output.
# please check with:
# xprop | grep -E '^WM_CLASS'
# or
# xwininfo -root -tree | grep -iE '(.*myapp.*)' where myapp is one 
# of the words to be added.
excludeapps=( tint2 conky xfce4-panel glava )

# array of commands (or functions) to execute before cycling through all windows
executes=( borgbackup-off transmission-off "audtool --mainwin-show off" amule-off )

# if this string is non-empty it denotes a filename to store debug info in.
#~ debug_log="$HOME/${me}.$(date +%s).debug"
debug_log=""

###########################################################################

#~ identifier=$(date +%s)
#~ identifier="urxvt${identifier%??}"
# the close-all-windows-wrapper is using this same identifier as window class.

#~ excludeapps_winid[j]="$(echo "ibase=10;obase=16;$WINDOWID"|bc)" # the window we're currently in

# window id to hex. variations:
# - xwininfo seems to not use leading zeroes, but a leading 0x
# - wmctrl seems to always pad to 8 digits + a leading 0x
#~ self="$(printf "0x%08x" "$WINDOWID")"

############################################################################
# FUNCTIONS

debug() {
    [ -w "$debug_log" ] && echo -e "$@" >> "$debug_log"
}
amule-off() {
while pidof -x amule >/dev/null 2>&1; do
echo "Waiting for amule to shut down"
sleep 1
done
}
transmission-off() {
    if pidof transmission-daemon >/dev/null 2>&1; then
        read -n1 -p "Do you want to close transmission-daemon [Y/n]? " answer
        [[ "$answer" =~ [Nn] ]] && return
        transmission-remote --authenv --exit
        echo "Waiting for transmission-daemon to shut down"
        while pidof transmission-daemon >/dev/null 2>&1; do
            sleep 0.1
            echo -n "."
        done
        echo " Success!"
    fi
}
borgbackup-off(){
borgdelay=10
if pidof -x borgbackup.sh >/dev/null 2>&1; then
	echo "Cannot exit while borgbackup.sh is running!"
    while pidof -x borgbackup.sh >/dev/null 2>&1; do
        echo "Checking again in $borgdelay seconds."
        sleep $borgdelay
    done
    while pidof -x borgbackup.sh >/dev/null 2>&1; do
        echo "Checking again in $borgdelay seconds."
        sleep $borgdelay
    done
    echo "Finished!"
    # Because the above might've taken a very long time,
    # wiggle the mouse to disable screensaver:
    xdotool mousemove_relative --sync -p 0 100; sleep 0.1; xdotool mousemove_relative --sync -p 180 100
fi
}
############################################################################
# MAIN

# simple dependency check
for dep in grep xwininfo wmctrl; do
	type -f $dep >/dev/null || exit 1
done

sep="$(stty size)"
sep="${sep#* }"
((sep > 10 )) && sep="$(for (( ; sep > 0 ; sep-- )); do printf "-"; done)" || sep="----------"

echo "Gracefully closing all windows."

debug "Debugging $me on $(date)\n$sep\n$(wmctrl -m 2>&1)\n$sep$(wmctrl -l 2>&1)\n$sep\n$(wmctrl -d 2>&1)\n$sep"

for cmd in "${executes[@]}"; do
    echo -e "$sep\nExecuting: $cmd"
    $cmd
done

excludeapps_winid=()
for (( i=0,j=0 ; i<${#excludeapps[@]} ; i++ ))
do
    debug "checking for app $i - ${excludeapps[i]}:"
    while read excludeapps_winid[j]
    do
        debug "Matching output line $j - ${excludeapps_winid[j]}"
        if [[ "x${excludeapps_winid[j]}" != "x" ]]
        then
            debug "Is on the list of excludeapps."
            excludeapps_winid[j]="${excludeapps_winid[j]%% *}"
            debug "Exclude app $j: ${excludeapps_winid[j]}"
            j=$((j + 1))
        else
            debug "Got an empty string :-("
        fi
    done <<< "$(xwininfo -root -tree -int | grep -i "(.*${excludeapps[i]}.*)")"
    # dump all window info, with IDs in decimal, and grep for classes/names,
    # in brackets like: ("urxvt" "URxvt")
done

# Add this window to the array of excluded apps
# WINDOWID uses the same decimal format, just append it to the array
excludeapps_winid=( ${excludeapps_winid[@]} $WINDOWID )

echo "$sep
List of classes/names to exclude: \"${excludeapps[@]}\" and self.
$sep
Window IDs to exclude: ${excludeapps_winid[@]}"

debug "$sep\nMatching 'wmctrl -l' output against excludeapps winids:"
while read -r line
do
    debug "$sep\nLine $i - $line"
    for (( i=0 ; i<${#excludeapps_winid[@]} ; i++ ))
    do
        excludeapps_winid[i]="0x$(printf '%08x' ${excludeapps_winid[i]})"
        debug "${line%% *} = ${excludeapps_winid[i]} - are they the same?"
        if [[ "${line%% *}" == "${excludeapps_winid[i]}" ]]
        then
            debug "Yes they are! Skipping!"
            line=''
            break
        else
            debug "No they aren't."
        fi
    done
    if [[ "x$line" != "x" ]]
    then
        echo -e "$sep\nClosing: ${line/$HOSTNAME/}"
        # go to the desktop in question...
        wmctrl -s "${line:10:3}"
        # ...and close:
        wmctrl -ic "${line%% *}"
        # checking if it's really closed now:
        if wmctrl -l | grep -E "^${line%% *}" >/dev/null 2>&1
        then
            debug "${line%% *} hasn't shut down yet. waiting..."
            # wait, presumably for user action:
            echo -n "Waiting . "
            while wmctrl -l | grep -E "^${line%% *}" >/dev/null 2>&1
            do
                sleep 0.1; echo -n '.'
            done
            echo
            debug "${line%% *} has finally shut down!"
        fi
    fi
done <<< "$(wmctrl -l)"

debug "$sep\nComing back to $me - winid $WINDOWID"
wmctrl -ia "0x$(printf '%08x' $WINDOWID)" >/dev/null 2>&1

debug "$sep\nThat's it - see what's left:\n$(wmctrl -m 2>&1)\n$sep
$(wmctrl -l 2>&1)\n$sep\n$(wmctrl -d 2>&1)\n$sep"

echo "$sep
Press <Enter> for \"$@\" or anything else to continue."
oldifs="$IFS"
IFS=
read -s -n 1 key
# -s: do not echo input character. -n 1: read only 1 character
if [ -z "$key" ]; then
    IFS="$oldifs"
    debug "Doing \"$@\""
    while :
    do
        $@
        sleep 2
    done
else
    IFS="$oldifs"
    debug "Doing Nothing"
fi
